#include "PersonsTable.h"

PersonsTable::PersonsTable() {
	this->tableLoaded = false;
	this->sharedMemoryCounter = 1;
	this->readersCounter = 0;
	this->dbAccess = new Semaphore((char*)SEMKEYFILEPATH, 1);
	this->counterAccess = new Semaphore((char*)SEM2KEYFILEPATH, 1);
	this->orderAccess = new Semaphore((char*)SEM3KEYFILEPATH, 1);
}

PersonsTable::~PersonsTable() {
	list< SharedMemory<TableRecord> >::iterator it = this->records.begin();
	while (it != this->records.end()) {
		it->free();
		++it;
	}

	this->dbAccess->destroy();
	this->counterAccess->destroy();
	this->orderAccess->destroy();

	delete this->dbAccess;
	delete this->counterAccess;
	delete this->orderAccess;
}

void PersonsTable::loadTableFromFile() {
	TableFile* file = new TableFile(false);
	TableRecord record;
	SharedMemory<TableRecord> memory;

	file->getReadLock();

	while (!file->isEOF()) {
		record = file->getNextRecord();

		int result = memory.create((char*)string(DEFAULTTABLEFILEPATH).c_str(), this->sharedMemoryCounter);
		if(result == 0) {
			memory.write(record);
			this->records.push_back(memory);
		} else {
			cout << "Fallo al cargar el registro " << record.id << " en memoria" << endl;
		}

		this->sharedMemoryCounter++;
	}

	file->unlock();
	
	this->records.sort(compareSharedMemory);
	this->tableLoaded = true;
	delete(file);
}

list<TableRecord> PersonsTable::getRecords() {
	this->orderAccess->p();
	this->counterAccess->p();

	if(this->readersCounter == 0) {
		this->dbAccess->p();
	}

	this->readersCounter++;

	this->orderAccess->v();
	this->counterAccess->v();

	list<TableRecord> tableRecords;

	list< SharedMemory<TableRecord> >::iterator it = this->records.begin();
	while(it != this->records.end()) {
		tableRecords.push_back(it->read());
		++it;
	}

	this->counterAccess->p();

	this->readersCounter--;
	if(this->readersCounter == 0) {
		this->dbAccess->v();
	}

	this->counterAccess->v();

	return tableRecords;
}

int PersonsTable::addRecord(TableRecord record) {
	this->orderAccess->p();
	this->dbAccess->p();
	this->orderAccess->v();

	SharedMemory<TableRecord> memory;

	int result = memory.create((char*)string(DEFAULTTABLEFILEPATH).c_str(), this->sharedMemoryCounter);

	if (result == 0) {
		record.id = this->getNewId();
		memory.write(record);
		this->records.push_back(memory);
		result = record.id;
	}

	this->sharedMemoryCounter++;

	this->dbAccess->v();

	return result;
}

void PersonsTable::persistTableToFile() {
	TableFile* file = new TableFile(true);

	file->getWriteLock();

	list< SharedMemory<TableRecord> >::iterator it = this->records.begin();
	while(it != this->records.end()) {
		file->addRecord(it->read());
		++it;
	}

	file->unlock();

	delete(file);
}

bool PersonsTable::isTableLoaded() {
	return this->tableLoaded;
}

bool PersonsTable::compareSharedMemory(SharedMemory<TableRecord>& leftItem, SharedMemory<TableRecord>& rightItem) {
	TableRecord leftRecord = leftItem.read();
	TableRecord rightRecord = rightItem.read();

	return leftRecord.id < rightRecord.id;
}

long PersonsTable::getNewId() {
	if(this->records.empty()) {
		return 1;
	} else {
		long newId = this->records.back().read().id;
		newId++;
		return newId;
	}
}

TableRecord PersonsTable::getRecord(long id) {
	this->orderAccess->p();
	this->counterAccess->p();

	if(this->readersCounter == 0) {
		this->dbAccess->p();
	}

	this->readersCounter++;

	this->orderAccess->v();
	this->counterAccess->v();

	TableRecord record;
	record.id = -1;
	strcpy(record.address, "");
	strcpy(record.name, "");
	strcpy(record.phone, "");

	bool found = false;

	list< SharedMemory<TableRecord> >::iterator it = this->records.begin();
	while( (it != this->records.end()) && (!found) ) {
		if((*it).read().id == id) {
			record = (*it).read();
			found = true;
		}
		++it;
	}

	this->counterAccess->p();

	this->readersCounter--;
	if(this->readersCounter == 0) {
		this->dbAccess->v();
	}

	this->counterAccess->v();

	return record;
}

TableRecord PersonsTable::deleteRecord(long id) {
	this->orderAccess->p();
	this->dbAccess->p();
	this->orderAccess->v();

	TableRecord record;
	record.id = -1;
	strcpy(record.name, "");
	strcpy(record.address, "");
	strcpy(record.phone, "");
	bool found = false;

	list< SharedMemory<TableRecord> >::iterator it = this->records.begin();
	while( (it != this->records.end()) && (!found) ) {
		if((*it).read().id == id) {
			record = (*it).read();
			(*it).free();
			it = this->records.erase(it);
			found = true;
		} else {
			++it;
		}
	}

	this->dbAccess->v();

	return record;
}
